package com.leedsoft.controller;

import com.google.common.collect.Lists;
import com.leedsoft.common.LruCache;
import com.leedsoft.common.activiti.ProcessDiagramGenerator;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/acti")
public class ActivitiController {

    private static final LruCache<String, byte[]> defImgCache = new LruCache<>();

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private HistoryService historyService;

    /**
     * 流程图测试页面
     **/
    @RequestMapping(params = "testPage")
    public ModelAndView testPage(String procInstId, String procDefId) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("procInstId", procInstId);
        modelAndView.addObject("procDefId", procDefId);
        modelAndView.setViewName("actiTestPage");
        return modelAndView;
    }

    /**
     * 获取流程实例图，高亮显示已执行节点、已执行顺序流、正在执行节点
     */
    @RequestMapping("getProcInstImg")
    public void getProcInstImage(String procInstId, HttpServletResponse response) {
        Assert.isTrue(StringUtils.isNotBlank(procInstId), "流程ID不可为空");
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        try {
            //  获取历史流程实例
            HistoricProcessInstance histProcInst = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(procInstId).singleResult();

            if (histProcInst == null) {
                throw new RuntimeException("获取流程实例ID[" + procInstId + "]对应的历史流程实例失败！");
            } else {
                // 获取流程定义
                ProcessDefinitionEntity procDef = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                        .getDeployedProcessDefinition(histProcInst.getProcessDefinitionId());

                List<String> activeActIds = runtimeService.getActiveActivityIds(procInstId);
                List<HistoricActivityInstance> hisActs = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(procInstId).orderByHistoricActivityInstanceId().asc().list();
                List<String> executedActIds = new ArrayList<>(Lists.transform(hisActs, e -> e.getActivityId()));
                executedActIds.removeAll(activeActIds);

                //获取实例流程图流
                writeImg(ProcessDiagramGenerator.generateInstDiagram(procDef, executedActIds, activeActIds), response);
            }
        } catch (Exception e) {
            throw new RuntimeException("获取流程实例图失败！", e);
        }
    }

    /**
     * 获取流程定义图
     */
    @RequestMapping("getProcDefImg")
    public void getProcDefImage(String procDefId, HttpServletResponse response) {
        Assert.isTrue(StringUtils.isNotBlank(procDefId), "流程定义ID不可为空");

        try {
            byte[] defImgBytes = defImgCache.get(procDefId);
            if (defImgBytes != null) {
                writeImg(new ByteArrayInputStream(defImgBytes), response);
                return;
            }

            ProcessDefinitionEntity procDef = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                    .getDeployedProcessDefinition(procDefId);
            InputStream imgSteam = ProcessDiagramGenerator.getDefDiagram(procDef);
            byte[] imgBytes = IOUtils.toByteArray(imgSteam);
            defImgCache.put(procDefId, imgBytes);

            writeImg(new ByteArrayInputStream(imgBytes), response);
        } catch (Exception e) {
            throw new RuntimeException("获取流程定义图失败！", e);
        }
    }

    private void writeImg(InputStream imageStream, HttpServletResponse response) throws Exception {
        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        IOUtils.copy(imageStream, os);
        os.close();
        imageStream.close();
    }


}
